﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Drawing;
//using Team6_Algorithms.AidingClasses;
//using Team6_Algorithms.Binary;

namespace TLD_LPR.Segmentation
{
    /// <summary>
    /// The blob detection to be used as segmentation algorithm.
    /// The input image should be having a white background, and black characters.
    /// This algorithm is based on team - 4's blob detection algorithm,
    /// But modified for our own purposes.
    /// 
    /// Made by: Team 4, modified by: Alexander Streng
    /// </summary>
    public class BlobDetection
    {
        //Innit the variables
        private static int[] inputpixels;
        private static int[] pixelslabeled; // pixels 

        private static int givenlabel;

        //arrays used for non recursive checking of all pixels
        private static int[] ToCheck1x;
        private static int[] ToCheck1y;

        private static int[] ToCheck2x;
        private static int[] ToCheck2y;

        private static int amountToCheckList1;
        private static int amountToCheckList2;

        private static int imageY, imageX;

        private static Blob curBlob;
        private static List<Blob> bloblist;

        /// <summary>
        /// Start the algorithm. Algorithm is run on a singe int array, which then labels each pixel, 
        /// and assigns it to a blob.
        /// </summary>
        /// <param name="sourceImage">The image on which the algorithm should run. This should be a 
        /// black / white image of a license plate</param>
        /// <returns>A List with each character as a seperate blob</returns>
        public static List<Blob> doAlgorithm(Bitmap sourceImage)
        {

            //initialize variables
            givenlabel = 0;

            amountToCheckList1 = 0;
            amountToCheckList2 = 0;

            imageX = sourceImage.Width;
            imageY = sourceImage.Height;

            ToCheck1x = new int[100000];
            ToCheck1y = new int[100000];

            ToCheck2x = new int[100000];
            ToCheck2y = new int[100000];

            curBlob = new Blob();
            bloblist = new List<Blob>();

            //Initialize input/output image
            Bitmap output = new Bitmap(sourceImage.Width, sourceImage.Height);

            inputpixels = Binaryze.MakeBinarySingleIntImageInverted(sourceImage);

            pixelslabeled = new int[sourceImage.Height * sourceImage.Width];

            //Start the growing selection
            startGrowingSelection();

            return bloblist;
        }

        /// <summary>
        /// In this function the whole image ( single int array ) is beeing checked, 
        /// and if an int hasnt been assigned to a blob yet ( and its a forground image ( a 1 ) )
        /// this will pass call the function checkarea to assign it a label, and check 8 - connected around it for 
        /// more unlabelled pixels.
        /// </summary>
        private static void startGrowingSelection()
        {
            givenlabel = 1;
            int curpixel;

            // process whole image
            for (int y = 1; y < imageY - 1; y++)
            {
                for (int x = 1; x < imageX - 1; x++)
                {
                    curpixel = (y * imageX) + x;

                    // Is there a forground pixel that hasnt been processed yet ? 
                    if (inputpixels[curpixel] == 1 && pixelslabeled[curpixel] == 0)
                    {
                        //initialize the blob and give first pixel his label
                        pixelslabeled[curpixel] = givenlabel;

                        curBlob.amountOfPixels++;

                        curBlob.label = givenlabel;

                        curBlob.maxx = curBlob.minx = x;
                        curBlob.maxy = curBlob.miny = y;

                        // start growing blob detection
                        if (checkarea(givenlabel, x, y, 1) == true) // returns true is more pixels are left in the image
                        {
                            //start processing the list
                            processList(givenlabel);
                            //after processlist, the blob has been found
                        }
                        else // blob was only 1 pixel large
                        {
                            curBlob = new Blob();
                            continue;
                        }

                        //initialize for new blob
                        bloblist.Add(curBlob); 
                        curBlob = new Blob();
                        givenlabel++;
                    }
                }
            }
        }

        /// <summary>
        /// This function was added to prevent the algorithm from a stack overflow.
        /// The algorithm used to be recurive, but will now put each pixel that has to be checked in either one of 2 lists.
        /// When the functions processlist is called, it will go through each of the 2 lists, and check 8 - connected around it
        /// to assign labels. This way the algorithm will work out a complete blob in a single pass.
        /// </summary>
        /// <param name="label"></param>
        private static void processList(int label)
        {
            Boolean ProcessablePixels;

            //main loop
            for (; ; )
            {
                ProcessablePixels = false;

                //process list 1
                for (int i = 0; i < amountToCheckList1; i++)
                {
                    ProcessablePixels = true;
                    checkarea(label, ToCheck1x[i], ToCheck1y[i], 2);
                }
                amountToCheckList1 = 0;

                // process list 2
                for (int i = 0; i < amountToCheckList2; i++)
                {
                    ProcessablePixels = true;
                    checkarea(label, ToCheck2x[i], ToCheck2y[i], 1);
                }
                amountToCheckList2 = 0;

                // both list were empty so there are no connecting pixels anymore ( get out of the main loop )
                if (ProcessablePixels == false)
                {
                    break;
                }
            }
        }
         
        /// <summary>
        /// Checks 8 - connected ( so each of the 8 pixels around the given pixel ) for pixels that not yet have been assigned a 
        /// label. After which it gives the given label.
        /// </summary>
        /// <param name="label">the label to be given to the pixels</param>
        /// <param name="xPos">the xposition of the pixel that is beeing checked</param>
        /// <param name="yPos">the yposition of the pixel that is beeing checked</param>
        /// <param name="insertToList">The list in which the pixel needs to be inserted</param>
        /// <returns>Returns false if there are no more pixels to assign labels to</returns>
        private static Boolean checkarea(int label, int xPos, int yPos, int insertToList)
        {
            // boolean to check if there are ANY new items to process
            Boolean changed = false;

            // check the 8 connected pixels around the startpixel
            for (int y = -1; y < 2; y++)
            {
                for (int x = -1; x < 2; x++)
                {
                    int curpixel = ((yPos + y) * imageX) + (xPos + x);

                    // stay inside the picture
                    if ((xPos + x) >= 0 &&
                        (xPos + x) < imageX &&
                        (yPos + y) >= 0 &&
                        (yPos + y) < imageY)
                    {
                        //there is a foreground pixel without a label
                        if (pixelslabeled[curpixel] == 0 && inputpixels[curpixel] == 1)
                        {
                            pixelslabeled[curpixel] = label;
                            curBlob.amountOfPixels++;

                            // keep track of the bounding box for the blob
                            if ((xPos + x) > curBlob.maxx)
                                curBlob.maxx = xPos + x;

                            if ((xPos + x) < curBlob.minx)
                                curBlob.minx = xPos + x;

                            if ((yPos + y) > curBlob.maxy)
                                curBlob.maxy = yPos + y;

                            if ((yPos + y) < curBlob.miny)
                                curBlob.miny = yPos + y;

                            //insert to list that is NOT currently being processed
                            if (insertToList == 1)
                            {
                                ToCheck1x[amountToCheckList1] = xPos + x;
                                ToCheck1y[amountToCheckList1] = yPos + y;

                                amountToCheckList1++;
                            }
                            else
                            {
                                ToCheck2x[amountToCheckList2] = xPos + x;
                                ToCheck2y[amountToCheckList2] = yPos + y;
                                amountToCheckList2++;
                            }

                            changed = true;
                        }
                    }
                }
            }

            return changed;
        }
    }
   
}